package com.example.lotterysystem.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.lotterysystem.common.enums.GlobalErrorCodeEnum;
import com.example.lotterysystem.common.enums.PrizeGradeEnum;
import com.example.lotterysystem.common.enums.UserIdentityEnum;
import com.example.lotterysystem.common.exception.GlobalException;
import com.example.lotterysystem.common.utils.DateUtils;
import com.example.lotterysystem.common.utils.JWTUtil;
import com.example.lotterysystem.common.utils.RegexUtil;
import com.example.lotterysystem.controller.param.PasswordLoginParam;
import com.example.lotterysystem.controller.param.UserLoginParam;
import com.example.lotterysystem.controller.param.UserRegisterParam;
import com.example.lotterysystem.controller.param.VerifyCodeLoginParam;
import com.example.lotterysystem.controller.result.UserHistoryActivityResult;
import com.example.lotterysystem.dao.dataobject.User;
import com.example.lotterysystem.dao.dataobject.Encrypt;
import com.example.lotterysystem.dao.dataobject.WinningRecord;
import com.example.lotterysystem.mapper.ActivityMapper;
import com.example.lotterysystem.mapper.UserMapper;
import com.example.lotterysystem.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.UserService;
import com.example.lotterysystem.service.VerifyCodeService;
import com.example.lotterysystem.service.dto.UserDTO;
import com.example.lotterysystem.service.dto.UserLoginDTO;
import com.example.lotterysystem.service.dto.UserRegisterDTO;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private VerifyCodeService verifyCodeService;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private ActivityMapper activityMapper;
    /**
     * 用户注册
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        // 校验注册信息
        checkRegisterInfo(param);

        // 加密数据
        User user = new User();
        user.setUserName(param.getName());
        user.setEmail(param.getEmail());
        user.setIdentity(param.getIdentity());
        user.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        if(StringUtils.hasText(param.getPassword())){
            user.setPassword(DigestUtil.md5Hex(param.getPassword()));
        }

        // 保存数据
        userMapper.insert(user);

        // 构造返回
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(user.getId());
        return userRegisterDTO;
    }

    /**
     * 用户登录
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO;
        if(param instanceof PasswordLoginParam loginParam){
            // 密码登录
            userLoginDTO = loginByPassword(loginParam);
        } else if(param instanceof VerifyCodeLoginParam loginParam){
            // 短信验证码登录
            userLoginDTO = loginByVerifyCode(loginParam);
        } else {
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "登录方式错误");
        }
        return userLoginDTO;
    }

    /**
     * 获取人员列表
     * @param identityEnum
     * @return
     */
    @Override
    public List<UserDTO> getUserInfoList(HttpServletRequest request, UserIdentityEnum identityEnum) {
        String token = request.getHeader("user_token");
        Integer thisUserId = JWTUtil.getIdByToken(token);
        String identity = identityEnum == null ? null : identityEnum.name();
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id", "user_name", "email", "phone_number", "identity")
                .orderBy(true, false,
                        " CASE WHEN id = " + thisUserId + " THEN 1 ELSE 0 END ",
                        "id");
        if(identity != null){
            wrapper = wrapper.eq("identity", identity);
        }
        List<User> userList = userMapper.selectList(wrapper);
        List<UserDTO> userDTOList = userList.stream().map(user -> {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(user.getId());
            userDTO.setUserName(user.getUserName());
            userDTO.setEmail(user.getEmail());
            userDTO.setPhoneNumber(user.getPhoneNumber().getValue());
            userDTO.setIdentity(UserIdentityEnum.forName(user.getIdentity()));
            return userDTO;
        }).toList();
        return userDTOList;
    }

    @Override
    public List<UserHistoryActivityResult> getUserActivities(Long userId) {
        if(userId == null){
            throw new GlobalException(GlobalErrorCodeEnum.USER_HISTORY_INFO, "userId 为空");
        }
        List<WinningRecord> winningRecords =
                winningRecordMapper.selectList(new QueryWrapper<WinningRecord>()
                .eq("winner_id", userId));
        if(CollectionUtils.isEmpty(winningRecords)){
            throw new GlobalException(GlobalErrorCodeEnum.USER_HISTORY_INFO, "该用户还没有中过奖");
        }
        List<UserHistoryActivityResult> list = winningRecords.stream()
                .map(winningRecord -> {
                    UserHistoryActivityResult result = new UserHistoryActivityResult();
                    result.setUserName(winningRecord.getWinnerName());
                    result.setActivityName(winningRecord.getActivityName());
                    result.setWinnerTime(DateUtils.dateFormat(winningRecord.getWinningTime()));
                    result.setPrizeName(winningRecord.getPrizeName());
                    result.setPrizeTier(PrizeGradeEnum.forName(winningRecord.getPrizeTier()).getValue());
                    return result;
        }).toList();
        return list;
    }

    private UserLoginDTO loginByVerifyCode(VerifyCodeLoginParam loginParam) {
        if(!RegexUtil.checkPhoneNumber(loginParam.getPhoneNumber())){
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "手机号格式有误");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("phone_number", new Encrypt(loginParam.getPhoneNumber()));
        User user = userMapper.selectOne(wrapper);

        // 校验用户信息
        if (user == null || (StringUtils.hasText(loginParam.getMandatoryIdentity())
                && !user.getIdentity().equalsIgnoreCase(loginParam.getMandatoryIdentity()))) {
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "用户不存在或身份信息异常");
        }

        // 校验验证码是否正确
        if(!loginParam.getVerifyCode().equals(verifyCodeService.getVerifyCode(loginParam.getPhoneNumber()))){
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "验证码错误或已过期");
        }
        return returnLoginDTO(user);
    }

    private UserLoginDTO loginByPassword(PasswordLoginParam loginParam) {
        User user;
        if(RegexUtil.checkMail(loginParam.getLoginName())) {
            // 通过邮箱作为用户名登录
            QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("email", loginParam.getLoginName());
            user = userMapper.selectOne(wrapper);
        } else if(RegexUtil.checkPhoneNumber(loginParam.getLoginName())){
            // 通过手机号作为用户名登录
            QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("phone_number", new Encrypt(loginParam.getLoginName()));
            user = userMapper.selectOne(wrapper);
        } else {
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "登录名不符合规范");
        }

        // 校验用户信息
        if(user == null
                || (StringUtils.hasText(loginParam.getMandatoryIdentity())
                        && !user.getIdentity().equalsIgnoreCase(loginParam.getMandatoryIdentity()))){
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "该用户不存在或身份信息有误");
        }
        if(!user.getPassword().equals(DigestUtil.md5Hex(loginParam.getPassword()))){
            throw new GlobalException(GlobalErrorCodeEnum.LOGIN_ERROR, "密码错误");
        }
        return returnLoginDTO(user);
    }

    /**
     * 构造返回 UserLoginDTO 对象
     * @param user
     * @return
     */
    private UserLoginDTO returnLoginDTO(User user){
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", user.getId());
        claim.put("username", user.getUserName());
        String token = JWTUtil.generateToken(claim);
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(user.getIdentity()));
        userLoginDTO.setUserName(user.getUserName());
        return userLoginDTO;
    }

    private void checkRegisterInfo(UserRegisterParam param) {
        if(param == null) {
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "param 为空");
        }

        // 校验邮箱格式
        if(!RegexUtil.checkMail(param.getEmail())) {
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "邮箱格式有误");
        }

        // 校验手机号格式
        if(!RegexUtil.checkPhoneNumber(param.getPhoneNumber())) {
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "手机号格式有误");
        }

        // 校验身份信息 admin/normal
        if(UserIdentityEnum.forName(param.getIdentity()) == null){
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "身份信息异常");
        }

        // 校验管理员密码是否已经必填, 并且是否符合密码格式
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.getType())){
            if(!StringUtils.hasLength(param.getPassword())) {
                throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "管理员密码为空");
            } else if(!RegexUtil.checkPassword(param.getPassword())){
                throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "管理员密码格式有误");
            }
        }

        // 校验用户名是否存在
        if(userMapper.selectOne(new QueryWrapper<User>()
                .eq("user_name", param.getName())) != null){
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "该用户已存在");
        }

        // 校验邮箱是否被使用
        if(checkMailIsUsed(param.getEmail())) {
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "邮箱已被使用");
        }

        // 校验手机号是否被使用
        if(checkPhoneIsUsed(param.getPhoneNumber())){
            throw new GlobalException(GlobalErrorCodeEnum.REGISTER_ERROR, "手机号已经被使用");
        }
    }

    private boolean checkMailIsUsed(String email){
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("email", email);
        Long res = userMapper.selectCount(wrapper);
        return res > 0;
    }

    private boolean checkPhoneIsUsed(String phoneNumber){
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("phone_number", new Encrypt(phoneNumber));
        return userMapper.selectCount(wrapper) > 0;
    }
}
